package site.zhouinfo.server;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 项目修改检测器
 *
 * @author liudajiang
 * @create_date 2016年7月14日
 */
public class ChangeScanner {
    /**
     * 检测间隔时间
     */
    private Long scanIntervalSeconds;

    /**
     * 调度线程池
     */
    private ScheduledExecutorService scheduledExecutorService;
    /**
     * 修改事件处理器
     */
    private AppChangeHandler changeHandler;
    /**
     * 排除检测解析器
     */
    private ExcudeScanResolver excudeScanResolver;
    /**
     * 修改触发文件<br/>
     * 此文件设置后ExcudeScanResolver将失效
     */
    private File triggerFile;
    /**
     * 上次检测结果<br/>
     * key:文件全路径<br/>
     * value:文件最后修改时间
     */
    private Map<String, Long> lastResult = new HashMap<String, Long>();
    /**
     * 本次检测结果<br/>
     * key:文件全路径<br/>
     * value:文件最后修改时间
     */
    private Map<String, Long> currentResult = new HashMap<String, Long>();

    public ChangeScanner() {
    }

    public void start() {
        ScannerTask scannerTask = new ScannerTask();
        scannerTask.addScanFile(AbstarctServer.getRootClassFile());
        getScheduledExecutorService().scheduleAtFixedRate(scannerTask, 0, scanIntervalSeconds,
                TimeUnit.SECONDS);
    }

    private void onchange() {
        if (changeHandler != null) {
            changeHandler.onChange();
        }
    }


    private class ScannerTask implements Runnable {

        List<File> files = new LinkedList<File>();


        /**
         * 增加检测文件<br/>
         * 如果文件为一个目录，会递归检测目录下所有文件
         *
         * @param file
         * @create_date 2016年7月14日
         */
        public void addScanFile(File file) {
            files.add(file);
        }

        public List<File> fromScanPath(String path) {
            List<File> files = new ArrayList<File>();
            String[] paths = null;
            if (path.indexOf(String.valueOf(File.pathSeparator)) != -1) {
                paths = path.split(File.pathSeparator);
            } else {
                paths = new String[]{path};
            }
            for (String eachPath : paths) {
                files.add(new File(eachPath));
            }
            return files;
        }

        @Override
        public void run() {
            scan();
            if (isChanged()) {
                System.out.println("检测到代码修改");
                onchange();
            }
            lastResult.clear();
            lastResult.putAll(currentResult);
            currentResult.clear();
        }

        private void scan() {
            List<File> scanFiles = new ArrayList<File>();
            if (triggerFile != null) {
                scanFiles.add(triggerFile);
            } else {
                scanFiles.addAll(files);
                //检测java classpath,用来检测项目依赖的 项目 或 类库
                scanFiles.addAll(fromScanPath(System.getProperty("java.class.path")));
            }
            Set<String> fileSet = new HashSet<String>();

            for (File file : scanFiles) {
                if (fileSet.contains(file.getAbsolutePath())) {
                    continue;
                }
                try {
                    scanFile(file);
                } catch (Exception e) {
                    System.out.println("检测文件：" + file.getAbsolutePath() + "失败");
                    e.printStackTrace();
                }
                fileSet.add(file.getAbsolutePath());
            }
        }

        /**
         * 与上次检测结果对比是否以改变
         *
         * @return true:不同 false:相同
         * @create_date 2016年7月11日
         */
        private boolean isChanged() {
            boolean result = false;
            for (Map.Entry<String, Long> entry : currentResult.entrySet()) {
                Long lastmodify = lastResult.get(entry.getKey());
                if (lastmodify == null) {
                    return false;
                }
                if (!entry.getValue().equals(lastmodify)) {
                    System.out.println("检测到修改文件：" + entry.getKey());
                    if (!result) {
                        result = true;
                    }
                }
            }
            return result;
        }

        /**
         * 获取文件的修改时间<br/>
         * 如果文件是目录，会递归获取目录下所有文件
         *
         * @param file
         * @create_date 2016年7月14日
         */
        private void scanFile(File file) {
            if (file == null || !file.exists()) {
                return;
            }
            if (file.isDirectory()) {
                for (File subFile : file.listFiles()) {
                    scanFile(subFile);
                }
                return;
            }
            if (triggerFile == null && excudeFile(file)) {
                return;
            }
            getCurrentResult().put(file.getAbsolutePath(), file.lastModified());
        }

        private boolean excudeFile(File file) {
            if (getExcudeScanResolver() != null && getExcudeScanResolver().isExcude(file)) {
                return true;
            }
            return false;
        }

        public Map<String, Long> getCurrentResult() {
            if (currentResult == null) {
                currentResult = new HashMap<String, Long>();
            }
            return currentResult;
        }

    }

    public Long getScanIntervalSeconds() {
        return scanIntervalSeconds;
    }

    public ChangeScanner setScanIntervalSeconds(Long scanIntervalSeconds) {
        this.scanIntervalSeconds = scanIntervalSeconds;
        return this;
    }

    public ScheduledExecutorService getScheduledExecutorService() {
        if (scheduledExecutorService == null) {
            scheduledExecutorService = Executors.newScheduledThreadPool(1);
        }
        return scheduledExecutorService;
    }

    public void setScheduledExecutorService(ScheduledExecutorService scheduledExecutorService) {
        this.scheduledExecutorService = scheduledExecutorService;
    }

    public ChangeScanner setOnchange(AppChangeHandler handler) {
        this.changeHandler = handler;
        return this;
    }

    public interface AppChangeHandler {
        public void onChange();
    }

    public ExcudeScanResolver getExcudeScanResolver() {
        return excudeScanResolver;
    }

    public ChangeScanner setExcudeScanResolver(ExcudeScanResolver excudeScanResolver) {
        this.excudeScanResolver = excudeScanResolver;
        this.excudeScanResolver.setLastResult(lastResult);
        return this;
    }

    public ChangeScanner setTriggerFile(File triggerFile) {
        this.triggerFile = triggerFile;
        return this;
    }
}
